﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Client.Properties;
using Common;
using InTheHand.Net;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using Newtonsoft.Json;
using System.Net.Sockets;

namespace Client
{
    public partial class Form1 : Form
    {
        private readonly char[] _record = new char[2048];
        private readonly char[] _buffer = new char[2048];
        private int _index;
        private bool _startCode;
        private Thread _worker;
        private TextWriter _connWtr;
        private volatile bool _workerRunning;
        private ConfigTransfer _currentConfiguration;

        public Form1()
        {
            InitializeComponent();
            SetCommandsEnabledState();
        }

        public void Listener(object stm)
        {
            var stream = (NetworkStream) stm;
            TextReader rdr = new StreamReader(stream);

            while (_workerRunning)
            {
                try
                {
                    var cnt = rdr.Read(_buffer, 0, 2048);
                    if (cnt > 0)
                    {
                        for (int i = 0; i < cnt; i++ )
                        {
                            var ch = _buffer[i];
                            if (ch == 0x0a)
                            {
                                _index = 0;
                                _startCode = true;
                                for (int x = 0; x < _record.Length; x++)
                                {
                                    _record[x] = '\0';
                                }
                            }
                            else if (ch == 0x0d)
                            {
                                if (_startCode == false)
                                {
                                    _index = 0;
                                }
                                else
                                {
                                    _startCode = false;
                                    _index = 0;
                                    var s = new string(_record);
                                    Log(s);
                                    var tuples = s.Split('|');
                                    switch (tuples[0])
                                    {
                                        case "Configuration":
                                            _currentConfiguration = JsonConvert.DeserializeObject<ConfigTransfer>(tuples[1]);
                                            Log(_currentConfiguration._lat.ToString("f4"));
                                            Log(_currentConfiguration._long.ToString("f4"));
                                            break;
                                        case "Position":
                                            var pos = JsonConvert.DeserializeObject<PositionTransfer>(tuples[1]);
                                            if (_currentConfiguration._dual)
                                            {
                                                SetElevation(pos.elevation.ToString("f4"));
                                            }
                                            SetAzimuth(pos.azimuth.ToString("f4"));
                                            Log(pos.azimuth.ToString("f4"));
                                            Log(pos.elevation.ToString("f4"));
                                            break;
                                    }

                                }
                            }
                            else if (_startCode)
                            {

                                _record[_index++] = ch;
                            }
                            if (_index >= _record.Length)
                            {
                                _index = 0;
                                _startCode = false;
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    var x = ex.Message;
                }
                Thread.Sleep(100);
            }
            rdr.Close();
        }

        #region BlueTooth Connection Set/Close

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (_connWtr == null)
            {
                var dlg = new InTheHand.Windows.Forms.SelectBluetoothDeviceDialog();
                if (dlg.ShowDialog(this) == DialogResult.OK)
                {
                    var s = dlg.SelectedDevice;
                    BluetoothConnect(s.DeviceAddress);
                }
            }

        }

        void BluetoothConnect(BluetoothAddress addr)
        {
            var cli = new BluetoothClient();
            try
            {
                cli.Connect(addr, BluetoothService.SerialPort);
                var peer = cli.GetStream();
                SetConnection(peer, true, cli.RemoteEndPoint);
                SetCommandsEnabledState();
                RequestConfiguration();
                RequestCurrentPosition();
            }
            catch (SocketException ex)
            {
                // Try to give a explanation reason by checking what error-code.
                // http://32feet.codeplex.com/wikipage?title=Errors
                // Note the error codes used on MSFT+WM are not the same as on
                // MSFT+Win32 so don't expect much there, we try to use the
                // same error codes on the other platforms where possible.
                // e.g. Widcomm doesn't match well, Bluetopia does.
                // http://32feet.codeplex.com/wikipage?title=Feature%20support%20table
                string reason;
                switch (ex.ErrorCode)
                {
                    case 10048: // SocketError.AddressAlreadyInUse
                        // RFCOMM only allow _one_ connection to a remote service from each device.
                        reason = Resources.ErrorExistingConnection;
                        break;
                    case 10049: // SocketError.AddressNotAvailable
                        reason = Resources.ErrorNoService;
                        break;
                    case 10064: // SocketError.HostDown
                        reason = Resources.ErrorNotRFCOMM;
                        break;
                    case 10013: // SocketError.AccessDenied:
                        reason = Resources.ERRORAuthenticationRequired;
                        break;
                    case 10060: // SocketError.TimedOut:
                        reason = Resources.ErrorTimeout;
                        break;
                    default:
                        reason = null;
                        break;
                }
                reason += " (" + ex.ErrorCode.ToString() + ") -- ";
                //
                var msg = Resources.ErrorConnectionFailed + ex.Message;
                msg = reason + msg;
                Log(msg);
                MessageBox.Show(msg);
            }
            catch (Exception ex)
            {
                var msg = Resources.ErrorConnectionFailed + ex.Message;
                Log(msg);
                MessageBox.Show(msg);
            }
        }


        private void SetConnection(Stream peerStream, bool outbound, BluetoothEndPoint remoteEndPoint)
        {
            if (_connWtr != null)
            {
                Log(Resources.LogAlreadyConnected);
                return;
            }
            _connWtr = new StreamWriter(peerStream);
            _connWtr.NewLine = "\r\n"; // Want CR+LF even on UNIX/Mac etc.
            _worker = new Thread(Listener);
            _workerRunning = true;
            _worker.Start(peerStream);
            Log(Resources.LogConnectedTo
                // Can't guarantee that the Port is set, so just print the address.
                // For more info see the docs on BluetoothClient.RemoteEndPoint.
                + remoteEndPoint.Address);
        }


        private void buttonDisconnect_Click(object sender, EventArgs e)
        {
            BluetoothDisconnect();
        }

        private void ConnectionCleanup()
        {
            var wtr = _connWtr;
            _connWtr = null;
            if (wtr != null)
            {
                try
                {
                    wtr.Close();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(Resources.ErrorConnectionCleanup + ex.Message);
                }
            }
            _workerRunning = false;
            _worker.Join(2000);
        }

        void BluetoothDisconnect()
        {
            Log(Resources.LogDisconnecting);
            ConnectionCleanup();
            SetCommandsEnabledState();
            labelAzimuthValue.Visible = false;
            labelAzimuth.Visible = false;
            labelElevationValue.Visible = false;
            labelElevation.Visible = false;

            Log(Resources.LogDisconnected);

        }

        private bool IsConnected
        {
            get
            {
                return _connWtr != null;
            }
        }

        private void SetCommandsEnabledState()
        {
            groupBoxCommands.Enabled = IsConnected;
            buttonDisconnect.Enabled = IsConnected;
            buttonConnect.Enabled = !IsConnected;
        }
        #endregion

        private void Log(string message)
        {
            // ------------------------------------------------------------------
            // Seperate calling threads
            if (InvokeRequired)
            {
                BeginInvoke(new OnLog(Log), new object[] { message });
                return;
            }
            textBoxReceive.AppendText(message);
            textBoxReceive.AppendText("\n");
        }
        public delegate void OnLog(string message);

        private void SetAzimuth(string message)
        {
            // ------------------------------------------------------------------
            // Seperate calling threads
            if (InvokeRequired)
            {
                BeginInvoke(new OnLog(SetAzimuth), new object[] { message });
                return;
            }
            labelAzimuthValue.Text = message;
            labelAzimuthValue.Visible = message.Length > 0;
            labelAzimuth.Visible = labelElevationValue.Visible;

        }
        private void SetElevation(string message)
        {
            // ------------------------------------------------------------------
            // Seperate calling threads
            if (InvokeRequired)
            {
                BeginInvoke(new OnLog(SetElevation), new object[] { message });
                return;
            }
            labelElevationValue.Text = message;
            labelElevationValue.Visible = message.Length > 0;
            labelElevation.Visible = labelElevationValue.Visible;
        }


        private void buttonSetDate_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Date|");
            sb.Append(DateTime.Now.ToString("yyyy-MM-dd"));
            sb.Append('\r');
            sb.Append('\n');
            sb.Append("Time|");
            sb.Append(DateTime.Now.ToString("HH:mm:ss"));
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_worker != null && _worker.IsAlive)
                _worker.Join(500);
            if (_connWtr != null)
                BluetoothDisconnect();
        }

        private void buttonMoveEast_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|East");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonMoveDown_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Down");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonMoveUp_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Up");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonMoveWest_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|West");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonTrack_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Track");
            sb.Append('\r');
            Send(sb.ToString());
        }


        private void buttonTest_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Test");
            sb.Append('\r');
            Send(sb.ToString());
        }


        private void buttonStop_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Stop");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void Send(string sb)
        {
            _connWtr.Write(sb);
            _connWtr.Flush();
        }

        private void buttonSave_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Save");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonLoad_Click(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|Load");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void RequestConfiguration()
        {
            _index = 0;
            _startCode = false;
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|GetConfiguration");
            sb.Append('\r');
            Send(sb.ToString());
        }

        private void buttonSetConfig_Click(object sender, EventArgs e)
        {
            var dlg = new FormSettings();
            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                var output = JsonConvert.SerializeObject(dlg.Config);
                var sb = new StringBuilder();
                sb.Append('\n');
                sb.Append("SetConfiguration|");
                sb.Append(output);
                sb.Append('\r');
                Send(sb.ToString());                
            }
        }


        

        private void RequestCurrentPosition()
        {
            var sb = new StringBuilder();
            sb.Append('\n');
            sb.Append("Command|GetPosition");
            sb.Append('\r');
            Send(sb.ToString());
        }


    }
}
